﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Reflection;
using Microsoft.Internal;

namespace System.ComponentModel.Composition.ReflectionModel
{
    internal class ReflectionMethod : ReflectionMember
    {
        private readonly MethodInfo _method;
        private Delegate _value;

        public ReflectionMethod(MethodInfo method)
            : base(method)
        {
            _method = method;
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool RequiresInstance
        {
            get { return !_method.IsStatic; }
        }

        public override Type ReturnType
        {
            get { return _method.ReturnType; }
        }

        public override ReflectionItemType ItemType
        {
            get { return ReflectionItemType.Method; }
        }

        public override object GetValue(object instance)
        {
            if (_value == null)
            {
                _value = GetDelegate(this._method, instance);
            }

            return _value;
        }

        private static Type[] _funcTypes = { typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>) };
        private static Type[] _actionTypes = { typeof(Action), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>) };

        private static Delegate GetDelegate(MethodInfo method, object instance)
        {
            ParameterInfo[] parameters = method.GetParameters();

            Assumes.IsTrue(parameters.Length <= 4);

            bool isVoid = method.ReturnType == typeof(void);
            Type[] typeArray = isVoid ? _actionTypes : _funcTypes;
            Type[] genericArgTypes = new Type[parameters.Length + (isVoid ? 0 : 1)];

            for (int i = 0; i < parameters.Length; i++)
                genericArgTypes[i] = parameters[i].ParameterType;

            if (!isVoid)
                genericArgTypes[parameters.Length] = method.ReturnType;

            Type delegateType = typeArray[parameters.Length].IsGenericType ?
                typeArray[parameters.Length].MakeGenericType(genericArgTypes) :
                typeArray[parameters.Length];

            return Delegate.CreateDelegate(delegateType, instance, method);
        }
    }
}
